package org.ztuffaha.amqsecurity;

/**
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import java.util.HashMap;
import java.util.List;
import java.io.*;
import java.util.*;


import javax.swing.text.html.HTMLDocument.Iterator;

/*
 * @author: ztuffaha
 */
public class AuthenticationMap
{

    HashMap<String, AuthEntry> userMap = new HashMap<String, AuthEntry>();
    HashMap<String, QueueEntry> queueMap = new HashMap<String, QueueEntry>();
    private final String encryptionScheme = StringEncrypter.DESEDE_ENCRYPTION_SCHEME;
    private List newQueues = new ArrayList();
    private List newUsers = new ArrayList();
    private static final Log LOG = LogFactory.getLog(AuthenticationMap.class);

    public void addAuthEntry(String userName, AuthEntry user)
    {
        if (userMap.containsKey(userName)) //the user existed before
        {
            //this condition is to test if the users in the file have been changed or not
            //if a file has been changed, we are checking for any modified user credentials

            //get existing user info
            AuthEntry existingUser = (AuthEntry) userMap.get(userName);

            //if encrypted flag is set to false, then the flag has changed and so did the password
            //so set the new password
            if (user.getEncrypted().compareTo("false") == 0)
            {
                existingUser.setPassword(user.getPassword());
            }
            //if the encrypted flag is set to true, then there shouldn't be anything that has changed
            //user cannot change an encrypted password without changing the encrypted flag
            //but just in case, let's handle this scenario
            else if (user.getEncrypted().compareTo("true") == 0)
            {
                try
                {
                    StringEncrypter encrypter = new StringEncrypter(encryptionScheme);
                    if (encrypter.encrypt(existingUser.getPassword()) != null)
                    {
                        String existingEncryptedPassword = encrypter.encrypt(existingUser.getPassword());
                        if (existingEncryptedPassword.compareTo(user.getPassword()) != 0)
                        {
                            //user has changed the password and did not set the 'encrypted' flag to
                            // true, warn them
                            //in memory
                            LOG.warn("Password change did not take effect. If user changed password, " +
                                    "they must set 'encrypted' flag to false");
                        }
                    }
                    else
                    {
                        LOG.warn("Password for user " + user.getName() + " has not been encrypted or has been changed; previous configurations" +
                                " are restored");
                    }
                }
                catch (StringEncrypter.EncryptionException e)
                {
                    e.printStackTrace(); 
                }
            }
            else
            {
                LOG.warn("Encrypted flag in users.xml should be either 'true' or 'false'");
            }

            newUsers.add(userName);
        }
        else
        {
            //new user is added
            //at startup, all maps are empty.
            //users.xml might have existed from before, so we check the encrypted flag
            if (user.getEncrypted().compareTo("true") == 0)
            {
                try
                {
                    StringEncrypter encrypter = new StringEncrypter(encryptionScheme);
                    String decryptedPassword = encrypter.decrypt(user.getPassword());
                    user.setPassword(decryptedPassword);
                }
                catch (StringEncrypter.EncryptionException e)
                {
                    e.printStackTrace(); 
                }
            }
            //if the encrypted flag is false, 'user' object is already populated and will
            //be put directly
            userMap.put(userName, user);
            newUsers.add(userName);
        }
    }

    public AuthEntry getAuthEntry(String userName)
    {
        return userMap.get(userName);
    }

    public void addQueueEntry(String queueName, QueueEntry queue)
    {
        if (queueMap.containsKey(queueName))
        {
            queueMap.remove(queueName);
        }
        queueMap.put(queueName, queue);
        newQueues.add(queueName);
    }

    public QueueEntry getQueueEntry(String queueName)
    {
        return queueMap.get(queueName);
    }

    public void setUserMap(HashMap authorMap)
    {
        userMap = authorMap;
    }

    public HashMap getUserMap(HashMap authorMap)
    {
        return userMap;
    }

    public boolean isValidUser(String userName, String password)
    {
        if (userMap.containsKey(userName))
        {
            AuthEntry auth = (AuthEntry) userMap.get(userName);

            if (auth.getPassword().compareTo(password) == 0)
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public boolean hasWriteAccess(String userName, String queueName)
    {
        if (userMap.containsKey(userName))
        {
            if (queueName.contains("ActiveMQ.Advisory"))
            {
                return true;
            }
            QueueEntry queue = (QueueEntry) queueMap.get(queueName);

            if (queue.getAdmins().contains(userName) || queue.getSenders().contains(userName))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public boolean hasReadAccess(String userName, String queueName)
    {
        if (userMap.containsKey(userName))
        {
            if (queueName.contains("ActiveMQ.Advisory"))
            {
                return true;
            }
            if (!queueMap.containsKey(queueName))
            {
                return false;
            }

            QueueEntry queue = (QueueEntry) queueMap.get(queueName);

            if (queue.getAdmins().contains(userName) || queue.getReceivers().contains(userName))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public boolean hasAdminAccess(String userName, String queueName)
    {
        if (userMap.containsKey(userName))
        {
            QueueEntry queue = (QueueEntry) queueMap.get(queueName);

            if (queue.getAdmins().contains(userName))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        else
        {
            return false;
        }
    }

    public List getAllUsres()
    {
        List usernames = new ArrayList();
        java.util.Iterator iterUsers = userMap.values().iterator();

        while (iterUsers.hasNext())
        {
            AuthEntry auth = (AuthEntry) iterUsers.next();
            usernames.add((String) auth.getName());
        }

        return usernames;
    }

    public List getAllQueues()
    {
        List queueames = new ArrayList();
        java.util.Iterator iterQueues = queueMap.values().iterator();

        while (iterQueues.hasNext())
        {
            QueueEntry auth = (QueueEntry) iterQueues.next();
            queueames.add((String) auth.getQueuename());
        }

        return queueames;
    }

    public void cleanUsers()
    {
        newUsers = new ArrayList();
    }

    public void cleanQueues()
    {
        newQueues = new ArrayList();
    }

    public void updateMaps()
    {
        List entriesToBeRemoved = new ArrayList();
        java.util.Iterator iterUsers = userMap.values().iterator();
        while (iterUsers.hasNext())
        {
            AuthEntry auth = (AuthEntry) iterUsers.next();
            if (!newUsers.contains((String) auth.getName()))
            {
                entriesToBeRemoved.add(auth.getName());
            }
        }

        java.util.Iterator itrRemoveUsers = entriesToBeRemoved.iterator();
        while (itrRemoveUsers.hasNext())
        {
            String user = (String) itrRemoveUsers.next();
            userMap.remove(user);
        }

        List queuesToBeRemoved = new ArrayList();
        java.util.Iterator iterQueues = queueMap.values().iterator();

        while (iterQueues.hasNext())
        {
            QueueEntry queue = (QueueEntry) iterQueues.next();
            if (!newQueues.contains((String) queue.getQueuename()))
            {
                queuesToBeRemoved.add(queue.getQueuename());
            }
        }
        java.util.Iterator itrRemoveQueues = queuesToBeRemoved.iterator();
        while (itrRemoveQueues.hasNext())
        {
            String queue = (String) itrRemoveQueues.next();
            queueMap.remove(queue);
        }


    }
}
